/*
 * $Id: ActiveRenderingThread.java 122 2009-05-12 15:54:07Z meyer@alphatec.de $
 * 
 *
 * Copyright 2009 DeltaDesk
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package deltadesk.gui.deltagui.graphics;

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
import java.awt.image.VolatileImage;

/**
 * ActiveRenderingThread.
 * 
 * This class uses active rendering and double buffering.
 * 
 * @author Andreas Meyer
 * @version $Revision: 122 $
 */
public class ActiveRenderingThread extends Thread {
	
	/** The default desired frames per second. */
	private static final int DEFAULT_DESIRED_FPS = 25;

	/** The double buffer strategy. */
	private final BufferStrategy bs;

	/** The unbuffered graphics context. */
	private final Graphics2D g2;
	
	/** The offscreen image. */
	private final VolatileImage bufferImage;

	/** The renderable object. */
	private final Renderable renderObject;
	
	/** The desired frames per second. */
	private final int desiredFPS;
	
	/** The width. */
	private final int width;
	
	/** The height. */
	private final int height;
	
	/** The running state of the thread. */
	private boolean running = true;
	
	/** Signals thread has stopped. */
	private boolean stopped;


	/**
	 * Construct the rendering thread.
	 * 
	 * @param bs
	 * @param g2
	 * @param renderObject
	 * @param window
	 */
	public ActiveRenderingThread(final BufferStrategy bs,
			                     final Graphics2D g2,
			                     final VolatileImage bufferImage,
                                 final Renderable renderObject,
                                 final int width,
                                 final int height) {
		
		this(bs, g2, bufferImage, renderObject, DEFAULT_DESIRED_FPS, width, height);
	}

	/**
	 * Construct the rendering thread.
	 * 
	 * @param bs
	 * @param g2
	 * @param renderObject
	 * @param desiredFPS
	 * @param window
	 */
	public ActiveRenderingThread(final BufferStrategy bs,
			                     final Graphics2D g2,
			                     final VolatileImage bufferImage,
			                     final Renderable renderObject,
			                     final int desiredFPS,
			                     final int width,
			                     final int height) {
		
		super("DeltaGUI-ActiveRenderer");
		
		this.bs           = bs;
		this.g2           = g2;
		this.bufferImage  = bufferImage;
		this.renderObject = renderObject;
		this.desiredFPS   = desiredFPS;
		this.width        = width;
		this.height       = height;
	}

	/**
	 * @param running the running to set
	 */
	public void setRunning(final boolean running) {
		this.running = running;
	}

	/**
	 * @return the stopped
	 */
	public boolean isStopped() {
		return stopped;
	}

	/**
	 * @see java.lang.Thread#run()
	 */
	@Override
	public void run() {
		
		final Rectangle clipRect = new Rectangle(0, 0, width - 1, height - 1);
		
		// timer stuff
		long timeDiff, sleepTime;
		long period = 1000 / desiredFPS;
		long beforeTime = System.currentTimeMillis();


		// the render loop
		while(running) {
			
			final Graphics2D graphicsContext;
			if(bs != null) {
				graphicsContext = (Graphics2D)bs.getDrawGraphics();
			} else if(bufferImage != null){
				graphicsContext = bufferImage.createGraphics();
			} else {
				graphicsContext = this.g2;
			} // end if
			
			// set antialiasing
			graphicsContext.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
			graphicsContext.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
					RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

			graphicsContext.setClip(clipRect);
			
			try {
				renderObject.render(graphicsContext);	
			} catch (final Exception e) {
				System.err.println("Caught Exception: " + e);
			}
			
			if(bs != null) {

				graphicsContext.dispose();

				// flip buffer to screen
				if(!bs.contentsLost()) {
					bs.show();
				} else {
					throw new RuntimeException("BufferStrategy lost contents");
				}
				
				// sync buffer and clean up
				Toolkit.getDefaultToolkit().sync();
			} // end if
			
			if(bufferImage != null) {
				g2.drawImage(bufferImage, 0, 0, null);
			}


			// calculate sleep time and sleep for a while
			// so that the VM can do other stuff
			if(period != -1) {
				timeDiff = System.currentTimeMillis() - beforeTime;

				sleepTime = period - timeDiff;
				if(sleepTime < 0) {
					sleepTime = 5;
				}
				GraphicsUtil.sleep(sleepTime);
				
				beforeTime = System.currentTimeMillis();
			} // end if

		} // end while
		
		stopped = true;
	}

}
